﻿#region Using Directives
using System;
using System.Collections.Generic;
using System.Collections;
using System.Data;
using System.Threading;
using System.Web;
using Dare.Utilities.Data;
using Stone.Data;

#endregion 

namespace Stone.Services
{
	/// <summary>
	/// Provides storage of global database connection information.
	/// </summary>
	public class ConnectionScope : IDisposable
	{
        //private static readonly string Key = typeof(ConnectionScope).Name;
		private  bool disposed;
        private static object syncRoot = new object();
        private Application application;
		/// <summary>
		/// Initializes a new instance of the ConnectionScope class.
		/// </summary>
		public ConnectionScope(Application app)
		{
            application = app;
		}

        ///// <summary>
        ///// Gets a reference to the ConnectionScope object for the current thread.
        ///// </summary>
        //public static ConnectionScope Current
        //{
        //    get
        //    {
        //        ConnectionScope scope = null;

        //        if ( HttpContext.Current != null )
        //        {
        //            scope = HttpContext.Current.Items[Key] as ConnectionScope;

        //            if ( scope == null )
        //            {
        //                scope = new ConnectionScope();
        //                HttpContext.Current.Items[Key] = scope;
        //            }
        //        }
        //        else
        //        {
        //            LocalDataStoreSlot slot = Thread.GetNamedDataSlot(Key);
        //            scope = Thread.GetData(slot) as ConnectionScope;

        //            if ( scope == null )
        //            {
        //                scope = new ConnectionScope();
        //                Thread.SetData(slot, scope);
        //            }
        //        }

        //        return scope;
        //    }
        //}

		#region Helper Methods

		/// <summary>
		/// Creates a new <see cref="Thread"/> object and copies
		/// the current <see cref="ConnectionScope"/> parameters.
		/// </summary>
		/// <param name="start">A delegate specifying which method to run
		/// when the <see cref="Thread"/> is started.</param>
		/// <returns>Returns a new <see cref="Thread"/> object.</returns>
		public Thread NewThread(ThreadStart start)
		{
            ConnectionScope scope = application.CurrentConnectionScope;

			Thread t = new Thread(delegate()
			{
				this.Copy(scope);
				start();
			});

			return t;
		}

		/// <summary>
		/// Creates a new <see cref="Thread"/> object and copies
		/// the current <see cref="ConnectionScope"/> parameters.
		/// </summary>
		/// <param name="start">A delegate specifying which method to run
		/// when the <see cref="Thread"/> is started.</param>
		/// <returns>Returns a new <see cref="Thread"/> object.</returns>
		public Thread NewThread(ParameterizedThreadStart start)
		{
            ConnectionScope scope = application.CurrentConnectionScope;

			Thread t = new Thread(delegate(Object obj)
			{
				this.Copy(scope);
				start(obj);
			});

			return t;
		}

		/// <summary>
		/// Queues a method for execution. The method executes
		/// when a thread pool thread becomes available.
		/// </summary>
		/// <param name="start">A delegate specifying which method to run
		/// when the <see cref="Thread"/> is started.</param>
		/// <returns>Returns true if the method is successfully queued.</returns>
		public bool EnqueueOnThreadPool(ThreadStart start)
		{
            ConnectionScope scope = application.CurrentConnectionScope;

			return ThreadPool.QueueUserWorkItem(delegate(object state)
			{
				this.Copy(scope);
				start();
			}, null);
		}

		/// <summary>
		/// Queues a method for execution. The method executes
		/// when a thread pool thread becomes available.
		/// </summary>
		/// <param name="start">A delegate specifying which method to run
		/// when the <see cref="Thread"/> is started.</param>
		/// <param name="state">An object containing data to be used by the method.</param>
		/// <returns>Returns true if the method is successfully queued.</returns>
		public bool EnqueueOnThreadPool(ParameterizedThreadStart start, object state)
		{
            ConnectionScope scope = application.CurrentConnectionScope;

			return ThreadPool.QueueUserWorkItem(delegate(object originalState)
			{
				this.Copy(scope);
				start(originalState);
			}, state);
		}

		/// <summary>
		/// Copies the values from the specified <paramref name="scope"/> object
		/// to the <see cref="ConnectionScope"/> used by the current thread.
		/// </summary>
		/// <param name="scope">A <see cref="ConnectionScope"/> object.</param>
		private void Copy(ConnectionScope scope)
		{
            ConnectionScope newScope = application.CurrentConnectionScope;
			newScope.DynamicConnectionString = scope.DynamicConnectionString;
            newScope.TransactionManager = scope.TransactionManager;
            newScope.DataProvider = scope.DataProvider;
		}

		/// <summary>
		/// Validates an existing <c cref="TransactionManager" /> if one exists,
		/// otherwise creates a new <c cref="TransactionManager" /> to use.
		/// </summary>
		public TransactionManager ValidateOrCreateTransaction()
		{
			return ValidateOrCreateTransaction(true);
		}

        public TransactionManager ValidateOrCreateTransaction(bool createTransaction)
        {
            return ValidateOrCreateTransaction(createTransaction, IsolationLevel.ReadCommitted);
        }
		
		/// <summary>
		/// Validates an existing <c cref="TransactionManager" /> if one exists,
		/// otherwise creates a new <c cref="TransactionManager" /> to use.
		/// </summary>
		/// <param name="createTransaction">determines whether to create a new transaction</param>
        public TransactionManager ValidateOrCreateTransaction(bool createTransaction, IsolationLevel level)
		{
            TransactionManager transactionManager = application.CurrentConnectionScope.TransactionManager;
			bool isBorrowedTransaction = (transactionManager != null && transactionManager.IsOpen);
			
			if (isBorrowedTransaction)
			{
				if (transactionManager == null || !transactionManager.IsOpen )
					throw new ArgumentException("The transactionManager is in an invalid state for this method.  \nYou must begin the tranasction prior to using this method.");

			}
			else if (createTransaction)
			{
                transactionManager = CreateTransaction(level);
			}
			else
			{
				// if current transaction is not valid, but we don't require one, return null
				transactionManager = null;
			}
			
			return transactionManager;
		}
		
		/// <summary>
		/// Creates a new transaction on the current <c>ConnectionScope</c>.
		/// </summary>
		public TransactionManager CreateTransaction()
		{
			return CreateTransaction(IsolationLevel.ReadCommitted);
		}
		
		/// <summary>
		/// Creates a new transaction on the current <c>ConnectionScope</c>
		/// with the specified <c cref="System.Data.IsolationLevel" />.
		/// </summary>
		/// <param name="level">Determines which <c cref="System.Data.IsolationLevel" /> to use for the transaction.</param>
		public TransactionManager CreateTransaction(IsolationLevel level)
		{
            application.CurrentConnectionScope.TransactionManager = application.CurrentConnectionScope.DataProvider.CreateTransaction();
			application.CurrentConnectionScope.TransactionManager.BeginTransaction(level);
			return application.CurrentConnectionScope.TransactionManager;
		}
		
		/// <summary>
        /// Completes this unit of work instance of the current transaction.  
        /// Commits the transaction,  Cleans up Connection, 
        /// Disposes of TransactionManager which performs unmanaged cleanup.
        /// </summary>
        /// <example>
        ///     try
        ///     {
        ///         using (ConnectionScope.CreateTransaction())
        ///         {
        ///             // or use ConnectionScope.ValidateOrCreateTransaction(true) 
        ///             // if you prefer to dynamically check for an existing transaction
        ///             AccountService.Save(account);
        ///             LedgerService.Save(generalEntry);
        /// 
        ///             //Commit and Cleanup your transaction
        ///             bool result = ConnectionScope.Complete();
        ///         }
        ///     }
        ///     catch (Exception ex)
        ///     {
        ///         // when creating the transaction via the 
        ///         // using (ConnectionScope.CreateTransaction()) construct, 
        ///         // there's no need to rollback, handled by 
        ///         // ConnectionScope Dispose Method.
        /// 
        ///         Logger.Write(ex.Message);
        ///     }
        ///</example>
        public bool Complete()
        {
            if (application.CurrentConnectionScope.disposed)
                throw new ObjectDisposedException("ConnectionScope");

            lock (syncRoot)
            {
                if (application.CurrentConnectionScope.HasTransaction)
                {
                    application.CurrentConnectionScope.TransactionManager.Commit();
					return true;
                }
				
				return false;
            }
        }
		

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            if (!disposed)
            {
                lock (syncRoot)
                {
                    disposed = true;

                    if (application.CurrentConnectionScope.HasTransaction)
                    {
                        application.CurrentConnectionScope.TransactionManager.Rollback();
                    }
                }
            }
        }

		#endregion Helper Methods

		#region Properties

		/// <summary>
		/// The DynamicConnectionString member variable.
		/// </summary>
		private string dynamicConnectionString;

		/// <summary>
		/// Gets or sets the DynamicConnectionString property.
		/// </summary>
		public string DynamicConnectionString
		{
			get { return dynamicConnectionString; }
			set { dynamicConnectionString = value; }
		}

		/// <summary>
		/// The TransactionManager member variable.
		/// </summary>
		private TransactionManager transactionManager;

		/// <summary>
		/// Gets or sets the TransactionManager property.
		/// </summary>
		public TransactionManager TransactionManager
		{
			get { return transactionManager; }
			set { transactionManager = value; }
		}
		
		/// <summary>
		/// The NetTiersProvider member variable.
		/// </summary>
        private IDataProviderManager dataProvider;
		
		/// <summary>
		/// Gets or Sets the Current DataProvider property of the <c>ConnectionScope</c> Object.
		/// </summary>
		/// <remarks>
		/// To use a dynamic connection, you must set both the 
		/// DynamicConnectionString and a unique ConnectionStringKey properties;
		///</remarks>
		public IDataProviderManager DataProvider
		{
			get
			{
                if (dataProvider == null)
                {
                    if (string.IsNullOrEmpty(DynamicConnectionString))
                    {
                        dataProvider = application.DataProviderManagerFactory.Default;
                    }
                    else
                    {
                        dataProvider = application.DataProviderManagerFactory.Default.CreateNew(DynamicConnectionString);
                    }
                }
				
				return dataProvider;
			}
			set
			{
				if (value != null)
				{
					dataProvider = value;	
				}
			}
		}

		/// <summary>
		/// Determines if Current Connections is in a Transaction.
		/// </summary>
		public bool HasTransaction
		{
            get { return application.CurrentConnectionScope.TransactionManager != null && application.CurrentConnectionScope.TransactionManager.IsOpen; }
		}
		#endregion Properties
	}

}